package uapp;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.naming.ConfigurationException;
import javax.servlet.ServletContext;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import uapp.configuration.ApplicationConfig;
import uapp.configuration.uAppConfig;

@SuppressWarnings("deprecation")
public class uAppContext {

	private static final String BASE_PATH_KEY = "uapp.uAppContext.BASE_PATH";
	private static final String CONFIG_FILE_KEY = "uapp.uAppContext.CONFIG_FILE";
	public static final String CURRENT_APP_PARAM = "a";
	public static final String CURRENT_PAGE_PARAM = "p";
	private static final String PARAMETER_EXCEPTION = "The parameter %s was not found in web.xml";
	private static final String THERE_IS_NO_AVAILABLE_APPS = "There is no available applications!";
	private static final String THERE_IS_NO_AVAILABLE_PAGE = "There is no page available for the application %s ";

	private List<Application> applications;
	private uAppConfig configuration;
	
	public static uAppContext getInstance() {
		FacesContext context = FacesContext.getCurrentInstance();
		ValueBinding binding = context.getApplication().createValueBinding("#{uapp}");
		return (uAppContext) binding.getValue(context);
	}

	private void ensureConfigPathParameter(Map<?, ?> parameters)
			throws ConfigurationException {
		if (!parameters.containsKey(CONFIG_FILE_KEY)) {
			String message = String.format(PARAMETER_EXCEPTION, CONFIG_FILE_KEY);
			throw new ConfigurationException(message);
		}
	}

	public List<Application> getApplications() {
		if (applications == null) {
			applications = new ArrayList<Application>();
			try {
				for (ApplicationConfig config : getConfiguration()
						.getApplications()) {
					applications.add(config.getInstance());
				}
			} catch (ConfigurationException e) {
				e.printStackTrace();
			}
		}
		return applications;
	}

	public uAppConfig getConfiguration() throws ConfigurationException {
		if (configuration == null)
			loadConfiguration();
		return configuration;
	}

	private File getConfigurationFile() throws ConfigurationException {
		ExternalContext ctx = getContext();
		Map<?, ?> parameters = ctx.getInitParameterMap();
		
		ensureConfigPathParameter(parameters);

		ServletContext servletContext = (ServletContext) ctx.getContext();
		String path = (String) parameters.get(CONFIG_FILE_KEY);
		String fullPath = servletContext.getRealPath(path);
		
		return new File(fullPath);
	}

	public Application getCurrentApplication() {
		Map<String, String> params = getRequestParameters();
		if (!params.containsKey(CURRENT_APP_PARAM)) {
			if (getApplications().size() >= 1)
				return getApplications().get(0);
		}
		for (Application app : getApplications()) {
			if (app.getId().equals(params.get(CURRENT_APP_PARAM)))
				return app;
		}
		throw new RuntimeException(THERE_IS_NO_AVAILABLE_APPS);
	}

	public Page getCurrentPage() {
		Map<String, String> params = getRequestParameters();
		Application app = getCurrentApplication();
		if (!params.containsKey(CURRENT_PAGE_PARAM)) {
			if (app.getPages().size() >= 1)
				return app.getPages().get(0);
		}
		for (Page page : app.getPages()) {
			if (page.getId().equals(params.get(CURRENT_PAGE_PARAM)))
				return page;
		}
		String message = String.format(THERE_IS_NO_AVAILABLE_PAGE, app.getName());
		throw new RuntimeException(message);
	}
	
	public String getCurrentLayout() throws ConfigurationException {
		return getBasePath() + getCurrentPage().getLayout() + Widget.EXTENSION;
	}
	
	public static String getBasePath() throws ConfigurationException {
		ExternalContext ctx = getContext();
		Map<?, ?> map = ctx.getInitParameterMap();
		if (!map.containsKey(BASE_PATH_KEY)) {
			String message = String.format(PARAMETER_EXCEPTION, BASE_PATH_KEY);
			throw new ConfigurationException(message);
		}
		return (String) map.get(BASE_PATH_KEY);
	}
	
	private static ExternalContext getContext() {
		FacesContext faces = FacesContext.getCurrentInstance();
		return faces.getExternalContext();
	}

	private Map<String, String> getRequestParameters() {
		ExternalContext ctx = getContext();
		return ctx.getRequestParameterMap();
	}

	private void loadConfiguration() throws ConfigurationException {
		try {

			JAXBContext jaxb = JAXBContext.newInstance(uAppConfig.class);
			Unmarshaller unmarshaller = jaxb.createUnmarshaller();
			Object config = unmarshaller.unmarshal(getConfigurationFile());
			setConfiguration((uAppConfig) config);
		} catch (JAXBException e) {
			e.printStackTrace();
		}
	}

	private void setConfiguration(uAppConfig configuration) {
		this.configuration = configuration;
	}

	
}
